<?php

namespace App\Controller;

use App\Library\System\Model\AppModel;
use App\Library\System\Model\CacheModel;
use App\Library\System\Model\CountryModel;
use App\Library\System\Model\LanguageModel;
use App\Library\System\Model\UserModel;
use App\Library\System\Sub\Auth;
use Zend\Http\Header\ContentType;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\JsonModel;
use Zend\View\Model\ViewModel;
use App\Library\System\Config;
use App\Library\System\Process;

/**
 * Base Controller Abstract Class
 *
 * @category   App
 * @package    App\Controller
 * @author     Loi Nguyen <loint@peslook.com>
 * @copyright  Peslook
 * @version    1.0
 * @since      Class available since Release 1.0
 */
abstract class BaseController extends AbstractActionController
{
    /**
     * Cache Model
     *
     * @var CacheModel
     */
    public $cache;
    /**
     * Layout Folder
     *
     * @var string
     */
    public $layout_folder;

    /**
     * Sets of less
     *
     * @var array cascading style sheet array
     */
    public $less = array();

    /**
     * Sets of less
     *
     * @var array cascading style sheet array
     */
    public $css;

    /**
     * Sets of js
     *
     * @var array javascript array
     */
    public $coffee = array();

    /**
     * Sets of less
     *
     * @var array cascading style sheet array
     */
    public $js;

    /**
     * Sets of css
     *
     * @var array cascading style sheet array
     */
    public $css_content;

    /**
     * Configuration
     *
     * @var $config
     */
    public $config;

    /**
     * Initialize Application
     * Init default components of application such as: js, css, title ...
     *
     */
    public function init()
    {
        $this->checkStatus();
        $this->out = $this->layout();

        // Configuration
        $this->config = Config::getInstance();

        // Global default params
        $this->out->title = 'Peslook';
        $this->out->description = '';
        $this->out->keyword = '';
        $this->out->angular = '';

        // Default CSS & Javascript  
        #$this->add('js', 'jquery', 'jquery.ui', 'angular', 'underscore','bootstrap', 'core','search', 'encode', 'func');
        #$this->debug($this->config->init);
        $this->coffee = array_merge($this->config->init->js, $this->coffee);
        $this->less = array_merge($this->config->init->css, $this->less);

        // Add custom global css
        if (!empty($this->css)) {
            foreach ($this->css as $style) {
                $this->add('css', $style);
            }
        }

        // Add custom global js
        if (!empty($this->js)) {
            foreach ($this->js as $script) {
                $this->add('js', $script);
            }
        }

        // Initialize Javascript Storage
        $this->js('app', '{}', false);
        $this->js('app.root', Config::getInstance()->site, true, false);

        // Initialize css
        $this->js('app.css', '{}', false, false);

        // Initialize multi-languages  
        $this->js('app.translate', '{}', false, false);

        // Security token
        $this->auth = new Auth();
        if ($this->auth->login) {
            $this->js('app.uid', $this->auth->currentUser(), false, false);
            $this->js('app.token', $this->auth->getSecurityToken(), true, false);
        }

    }

    /**
     * Security for package
     *
     */
    public function secure($package)
    {
        // TODO
    }

    /**
     * Func
     * Add function and config can be used by the view
     *
     */
    public function func($view)
    {
        $view->config = Config::getInstance();
        $view->process = Process::getInstance();
    }


    /**
     * Set Layout
     * Choose the folder layout and custom css, js in its view
     *
     */
    public function setLayout($layout, $lesses = array(), $coffees = array())
    {
        $this->cache = CacheModel::getInstance();
        $this->layout_folder = 'app/' . $layout;

        foreach ($lesses as $key => $value) {
            $lesses[$key] = 'modules/' . $lesses[$key];
        }

        foreach ($coffees as $key => $value) {
            if (strpos($value, 'lib/') === false) {
                $coffees[$key] = 'modules/' . $coffees[$key];
            }
        }

        $this->coffee = $coffees;
        $this->less = $lesses;
    }

    /**
     * Compile CoffeeScript to Javascript
     *
     * @return string Javascript file name
     */
    public function compileCoffee()
    {
        $js_content = '';
        $js_name = '';
        $folder = Config::getInstance()->root . '/js';

        foreach($this->coffee as $_coffee) 
        {    
            $js_name .= $_coffee.'-';                                 
        }    
            
        $js_name = md5($js_name);

        if ((!$this->config->isProduction()) || (!file_exists($folder . $js_name . '.js'))) {
            foreach ($this->coffee as $_coffee) {
                if (strpos($_coffee, 'lib/') === false) {

                    $cmd = "coffee -c -b $folder/$_coffee.coffee";
                    # sudo ln -s /usr/local/bin/coffee /usr/bin/coffee
                    exec($cmd);
                }
                $js_content .= file_get_contents("$folder/$_coffee.js");
                $js_content .= "\n \n";
            }

            file_put_contents("$folder/script/$js_name.js", $js_content);
        }

        return $this->config->site . '/js/script/' . $js_name . '.js';
    }

    /**
     * Compile Less to Css
     * Include default less file and custom less file
     *
     * @return string CSS Content
     */
    public function compileLess()
    {
        $less_config_file = $this->config->root . '/css/config.less';
        $less_config = file_get_contents($less_config_file);

        // Modify configuration
        $less_variable = array(
            'img'  => $this->config->img . '/app/app',
            'font' => $this->config->font
        );

        foreach ($less_variable as $variable => $value) {
            $replace_variable = "@$variable: '$value/';";
            $regex = '/^[\@[' . $variable . ']+:\s\'[a-z0-9\:.\/]+\';/';
            $less_config = preg_replace($regex, $replace_variable, $less_config);
        }

        file_put_contents($less_config_file, $less_config);

        $less_string = '';
        $less_name = '';

        foreach ($this->less as $less_file) {
            $less_string .= "@import '" . $less_file . "'; ";
            $less_name .= $less_file . '-';
        }

        if (!$this->config->isProduction()) {
            $style = Config::getInstance()->root . '/css/styles';
            file_put_contents($style . '.less', $less_string);

            # sudo ln -s /usr/local/bin/lessc /usr/bin/lessc  
            $cmd = "lessc -x --clean-css $style.less > $style.css";
            exec($cmd);
            $css_content = file_get_contents("$style.css");
            $this->cache->createCache('css', $less_name, $css_content);
        } else {
            $css_content = $this->cache->loadCache('css', $less_name);
        }

        return $css_content;
    }

    public function setAjaxLayout($view, $name, $js = null)
    {
        $this->layout()->ajax = true;
        $view->layout_folder = 'app/' . $name;
        $view->js = $js;
    }

    /**
     * get - $_GET
     * get is used instead of PHP - $_GET, Unless $key, it will return all of $_GET params
     *
     * @param string $key
     *
     * @return mixed object | string
     */
    public function get($key = null)
    {
        return is_null($key) ? $this->array2object($this->params()->fromQuery()) : $this->params()->fromQuery($key);
    }

    /**
     * post - $_POST
     * post is used instead of PHP - $_POST, Unless $key, it will return all of $_POST params
     *
     * @param string $key
     *
     * @return mixed object|string
     */
    public function post($key = null)
    {
        return is_null($key) ? $this->array2object($this->params()->fromPost()) : $this->params()->fromPost($key);
    }

    /**
     * router
     * router is used for $_GET in virtual path
     *
     * @param string $key
     *
     * @return mixed object|string
     */
    public function route($key = null)
    {
        return is_null($key) ? $this->array2object($this->params()->fromRoute()) : $this->params()->fromRoute($key);
    }

    /**
     * Convert array to object
     *
     * @param mixed     $array
     * @param ViewModel $page
     */
    public function array2object($array)
    {
        $object = new \stdClass();
        foreach ($array as $key => $value) {
            $object->$key = $value;
        }

        return $object;
    }

    /**
     * view
     * Pass data array to page
     *
     * @param mixed     $array
     * @param ViewModel $page
     */
    public function view($page, $array)
    {
        foreach ($array as $key => $value) {
            $page->$key = $value;
        }
    }

    /**
     *
     * Pass data array
     *
     * @param mixed     $array
     * @param ViewModel $page
     */
    public function add($key)
    {
        $num = func_num_args();
        if ($num < 2) {
            return;
        }

        $list = func_get_args();
        $arr = $this->out->$key;

        for ($i = 1; $i < $num; $i++) {
            $arr[] = $list[$i];
        }

        $this->out->$key = $arr;
    }

    // Support Javascript data inline to pass data from PHP to JS

    public function js($key, $value, $quote = false, $initialize = true)
    {
        $str = $this->out->script;

        if ($quote) {
            $value = '"' . $value . '"';
        }

        $str .= ($initialize ? 'var ' : '') . $key . '=' . $value . ';';
        $this->out->script = $str;
    }

    /**
     * Angular
     * Integrate angular module
     *
     * @param string $module
     */
    public function angular($module)
    {
        $angular = $this->out->angular;
        $angular .= $module . '';
        $this->out->angular = $angular;
    }

    /**
     * Script (raw script)
     * Add javascript to front-end
     *
     * @param string $javascript
     */
    public function script($javascript)
    {
        $script = $this->out->script;
        $script .= $javascript;
        $this->out->script = $script;
    }

    /**
     * JSON
     * Support for JSON Data (one or multi dimesional)
     *
     * @param
     * @param ViewModel $page
     */
    public function json($data, $model = true)
    {
        $result = null;

        if ($model) {
            // return JSON Response, e.g ajax request
            $result = new JsonModel($data);
        } else {
            if (is_array($data[0])) {
                $tmp = array();
                foreach ($data as $row)
                    $tmp[] = $row;
                $data = $tmp;
            }

            $result = json_encode($data);

            //$result = preg_replace("/\\\\u([a-f0-9]{4})/e", "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", json_encode($data));
        }

        // Return json string
        return $result;
    }

    /**
     * Create new page
     *
     * @param
     * @param ViewModel $page
     */
    public function createPage($page_name)
    {
        $this->init();
        $this->out->css_content = $this->compileLess();
        $this->out->js_name = $this->compileCoffee();

        // Setup layout
        $page = new ViewModel();
        $page->setTemplate($this->layout_folder . '/layout.phtml');

        // Create page content
        $content = new ViewModel();
        $content->setTemplate($this->layout_folder . '/' . $page_name . '.phtml');
        $content->abc = '';

        // Page content in layout
        $page->addChild($content);

        return $page;
    }

    /**
     * Get child in page
     *
     * @param $page
     *
     * @return ViewModel
     */
    public function getContentView($page)
    {
        $child = $page->getChildren();

        return $child[0];
    }

    public function support($page)
    {
        $app = AppModel::getInstance();
        $language = new LanguageModel();
        $country = new CountryModel();

        $this->js('app.languages', $this->json($language->getAllLanguages()->toArray(), false), false, false);
        $page->language = new \stdClass();
        $page->language->current = $app->getCurrentLanguage()->name;
        $page->language->region  = $country->getAllRegions();
        $page->language->total = 20;

        $page->login = $this->auth->login;
        $page->user = new UserModel($this->auth->currentUser());
    }

    // Send error code to browser
    public function error($code)
    {
        $this->out->title = '404 Page not found !';
        $this->out->description = '404 Page not found !';
        $this->layout()->global = true;
        $this->getResponse()->setStatusCode($code);
    }

    // Redirect to other page, support internal and external url
    public function go($url, $external = false)
    {
        if (!$external) {
            $config = Config::getInstance();
            $url = $config->site . $url;
        }
        $this->redirect()->toUrl($url);
    }

    public function back()
    {
        $this->go($this->getRequest()->getHeader('Referer')->getUri(), true);
    }

    public function debug($variable)
    {
        Process::debug($variable);
    }

    // Application status is maintain
    public function maintain()
    {
        die('This website is maintain');
    }

    // Pass variable from Controller to Global layout
    public function out($type, $str)
    {
        $header = new ContentType();
        $header->value = $type . '; charset=utf-8';
        $this->getResponse()->getHeaders()->addHeader($header);

        return $this->response->setContent($str);
    }

    public function setCache($conditions)
    {
        $_SESSION['cache'] = true;
        $hash = '';
        foreach ($conditions as $key => $value) {
            $hash = $key . '=' . $value . ';';
        }
        $_SESSION['cache_condition'] = md5($hash);
    }

    /**
     * Translate language to browser
     *
     * @param $variable
     * @param $string
     */
    public function lang($variable, $string)
    {
        $this->js('app.translate.' . $variable, $this->translate($string), true, false);
    }

    /**
     * Load Service
     *
     * @param $class
     * @return array|object
     */
    public function service($class)
    {
        $sm = $this->getServiceLocator();
        return $sm->get($class);
    }

    // Check mode of application
    public function checkStatus()
    {
        $mode = Config::mode;
        switch ($mode) {
            case 0:
                break;
            case 1:
                $this->maintain();
                break;
            default:
                return $this->error(404);
        }
    }

}
